#pragma once

#include "containers.h"
#include "position.hpp"
#include <cstdint>
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <stdint.h>
#include <vector>

namespace Trade {

struct Order
{
  dir_t d = 0; // -1 short, 1 long
  int vol = 0; // volume
  time_t tm = 0;
  int64_t idx = 0; // index of data
  price_t hpx = 0; // price in hop  = price / hop
  std::string user_oid;

  bool operator==(const Order& o) const noexcept
  {
    return (d == o.d && std::abs(hpx - o.hpx) < 5);
  }

  bool operator<(const Order& o) const noexcept
  {
    return (d == o.d && (hpx - o.hpx) * d < 0);
  }
};

using OrderPtr = std::shared_ptr<Order>;
using OrderList = std::list<OrderPtr>;

using OrderSet = PointerSet<Order, 1000>;
using OrderIndex = std::map<std::string, Order*>;

struct Deal
{
  int action = 0; // 0 for open, 1 for close
  dir_t d = 0;
  int vol = 0;     // volume of thid deal  , 2
  int pos_vol = 0; // volume in position   , old vol + new vol
  time_t tm = 0;
  int64_t idx = 0;
  price_t hpx = 0;
  price_t avg_hpx = 0;     // average price in hop
  price_t rvn_hop = 0;     // reverue in hop, float
  price_t revenue = 0;     // close revenue , closed
  price_t rvn_max_win = 0; // max-win-revenue
  time_t tm_max_win = 0;
  int64_t idx_max_win = 0;
  price_t rvn_max_lost = 0; // max-lost
  time_t tm_max_loss = 0;
  int64_t idx_max_loss = 0;

  void print() noexcept;
  void reset() noexcept { memset((void*)(this), 0, sizeof(Deal)); }
};

using DealPtr = std::shared_ptr<Deal>;
using DealVector = std::vector<DealPtr>;

struct Revenue
{
  price_t float_up = 0;  // float revenue of Long positions
  price_t float_dn = 0;  // float revenue of short positions
  price_t float_rvn = 0; // all float revune
  price_t close_up = 0;  // closed
  price_t close_dn = 0;
  price_t close_rvn = 0;
  price_t all = 0; // all revnue
  int64_t idx = 0; // current index of data

  void print(const int64_t idx) const noexcept;
  void reset() noexcept { memset((void*)(this), 0, sizeof(Revenue)); }
};

using OnOpenFunc = std::function<int(const OrderPtr o, const DealPtr d)>;
using OnCloseFunc = std::function<int(const OrderPtr o, const DealPtr d)>;
using OnPlacedFunc = std::function<int(const OrderPtr o)>;
using OnCancelunc = std::function<int(const OrderPtr o)>;

// mock interface to Exchange
class MockIF
{
public:
  //
  // mock interface
  //
  OrderList oo_up; // open-order for long
  OrderList oo_dn; // open-order for short
  OrderList co_up; // close-order for long
  OrderList co_dn; // close-order for short

  DealVector dv;   // Recodes of all deal
  Revenue revenue; // Real-time revenue

  Position pos_up; // Long-Position
  Position pos_dn; // Short-Position

public:
  //
  // mock interface
  //
  int mock_deal(const int64_t idx,
                const price_t hpx,
                const price_t ftime,
                int v[4]) noexcept;

  int mock_try_open(const dir_t d,
                    const price_t hpx,
                    const time_t tm,
                    const int64_t idx) noexcept;

  int mock_try_close(const dir_t d,
                     const price_t hpx,
                     const time_t tm,
                     const int64_t idx) noexcept;

  int mock_place_open_order(const dir_t d,
                            const price_t hpx,
                            const price_t hp_now,
                            const int vol,
                            const time_t tm,
                            const int64_t idx) noexcept;

  int mock_place_close_order(const dir_t d,
                             const price_t hpx,
                             const price_t hp_now,
                             const int vol,
                             const time_t tm,
                             const int64_t idx) noexcept;

  /// return amount of orders
  int mock_query_open_order(const dir_t d, const int64_t idx) noexcept;
  /// return amount of orders
  int mock_query_close_order(const dir_t d, const int64_t idx) noexcept;
  int mock_recall_all_order(const int64_t idx) noexcept;
  int mock_recall_all_open_order(const dir_t d, const int64_t idx) noexcept;
  int mock_recall_all_close_order(const dir_t d, const int64_t idx) noexcept;

  void mock_update_float_revenue(const price_t hpx) noexcept;

  bool mock_query_position(const dir_t d, int* vol, price_t* avg_hpx) noexcept;
  int mock_query_position_volume(const dir_t d) noexcept;
  bool mock_query_revenue(const dir_t d,
                          price_t& float_revenue,
                          price_t& close_revenue) noexcept;
  Position& mock_got_position(const dir_t d) noexcept
  {
    return (d > 0) ? pos_up : pos_dn;
  }

  void mock_print_orders_() const noexcept;

  Position* mock_get_position_(const dir_t d) noexcept
  {
    return (d > 0) ? &pos_up : &pos_dn;
  }
  OrderList* mock_get_order_list_(const dir_t d, const bool is_open) noexcept
  {
    if (is_open) {
      return (d > 0) ? &oo_up : &oo_dn;
    } else {
      return (d > 0) ? &co_up : &co_dn;
    }
  }

  void mock_show_deal_record() noexcept;
  void mock_close_all_position(const price_t hpx,
                               const price_t hp_now,
                               const time_t tm,
                               const int64_t idx) noexcept;
  void mock_close_position(const dir_t d,
                           const price_t hpx,
                           const price_t hp_now,
                           const time_t tm,
                           const int64_t idx) noexcept;

  bool is_good_close_price(const dir_t d, const price_t hpx) noexcept;

public:
  OnOpenFunc on_open = nullptr;
  OnCloseFunc on_close = nullptr;
  OnPlacedFunc on_placed = nullptr;
  OnCancelunc on_canceled = nullptr;
};

} // namespace Trade